// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "MsgFragment.h"
#include "Log/logging.h"
/*lint -save -e1401 */
/**
 * @brief: constructor
 */
MsgFragment::MsgFragment() {}
/*lint -restore */
/**
 * @brief: deconstructor
 */
MsgFragment::~MsgFragment() {}

/**
 * @brief: init message what to be send
 * @param [in] messageHead: message
 * @prama [in] type: type
 * @param [in] dataLen: data length
 */
void MsgFragment::InitSendMsg(StreamMsgHead& messageHead, uint8_t flag, VMIMsgType type, uint32_t dataLen)
{
    messageHead.setMagicWord();
    messageHead.setCheckSum(CREATE_VMI_CHECKSUM(static_cast<uint8_t>(type), flag));
    messageHead.setType(type);
    messageHead.setFlag(flag);
    messageHead.setPayloadSize(dataLen);
}

/*
 * @brief: Set whether to send the whole package without going through the fragmentation process.
 * @param [in] flag：True means that the whole packet is sent regardless of the data.
 */
void MsgFragment::SetSingleSend(bool flag)
{
    INFO("set singleSend flag %d", flag);
    m_singleSend = flag;
}

/**
 * @brief: Network message fragmentation. Note that the parameter buf points to the stream_msg_head header.
 *         Datalen only contains the data part. When successful, the return value is the length of the data
 *         part, i.e. datalen. If the return value is less than 0, it fails.
 * @param [in] type: type
 * @param [in] socket: socket
 * @param [in] buf: data
 * @param [in] dataLen: data length, no contain StreamMsgHead
 * @return: dataLen or error code
 */
int MsgFragment::FragmentSend(VMIMsgType type, std::shared_ptr<VmiSocket> socket, StreamMsgHead& buf, int dataLen)
{
    if ((dataLen <= 0) || (type <= VMIMsgType::Invalid) || (type >= VMIMsgType::End) ||
        (static_cast<uint64_t>(dataLen) > static_cast<uint64_t>(MAX_MSG_SIZE - sizeof(StreamMsgHead)))) {
        ERR("msgtype(%u) or buf(%p) or dataLen(%d) is error", type, &buf, dataLen);
        return -1;
    }

    if (socket == nullptr) {
        ERR("failed to send data, vmiSocket is nullptr");
        return -1;
    }

    StreamMsgHead* messageHead = &buf;

    if (static_cast<uint64_t>(dataLen) <= static_cast<uint64_t>(FRAGMENT_SIZE) || m_singleSend) {
        InitSendMsg(*messageHead, SINGLE_FRAGMENT, type, static_cast<uint32_t>(dataLen));
        VmiBuffer sendBuf(reinterpret_cast<uint8_t *>(messageHead), dataLen + sizeof(StreamMsgHead));
        int ret = socket->Send(sendBuf);
        if (ret != static_cast<int>(dataLen + sizeof(StreamMsgHead))) {
            LOG_RATE_LIMIT(ANDROID_LOG_ERROR, 1, "VMISOCKET send failed");
            return -1;
        }
    } else {
        InitSendMsg(*messageHead, FIRST_FRAGMENT, type, static_cast<uint32_t>(FRAGMENT_SIZE));
        VmiBuffer sendBuf(reinterpret_cast<uint8_t *>(messageHead), FRAGMENT_SIZE + sizeof(StreamMsgHead));
        if (socket->Send(sendBuf) < 0) {
            ERR("VMISOCKET send failed");
            return -1;
        }

        uint32_t len = dataLen - FRAGMENT_SIZE;
        messageHead = reinterpret_cast<StreamMsgHead *>(reinterpret_cast<uint8_t *>(messageHead) + FRAGMENT_SIZE);
        while (len > FRAGMENT_SIZE) {
            InitSendMsg(*messageHead, MIDDLE_FRAGMENT, type, static_cast<uint32_t>(FRAGMENT_SIZE));
            sendBuf = { reinterpret_cast<uint8_t *>(messageHead), FRAGMENT_SIZE + sizeof(StreamMsgHead) };
            if (socket->Send(sendBuf) < 0) {
                ERR("VMISOCKET send failed");
                return -1;
            }

            len = len - FRAGMENT_SIZE;
            messageHead = reinterpret_cast<StreamMsgHead *>(reinterpret_cast<uint8_t *>(messageHead) + FRAGMENT_SIZE);
        }

        InitSendMsg(*messageHead, END_FRAGMENT, type, static_cast<uint32_t>(len));
        sendBuf = { reinterpret_cast<uint8_t *>(messageHead), len + sizeof(StreamMsgHead) };
        if (socket->Send(sendBuf) < 0) {
            ERR("VMISOCKET send failed");
            return -1;
        }
    }

    return dataLen;
}
